package com.nega.commons.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * Util - 时间工具类
 * 
 * @author Garc
 * 
 */
public class DateUtils {
	private static final Logger LOG = LoggerFactory.getLogger(DateUtils.class);

	private DateUtils() {

	}

	private static Date defaultDate;

	private static String[] optionDateFormats = new String[] { "yyyy-MM-dd HH:mm:ss.S a", "yyyy-MM-dd HH:mm:ssz", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm:ssa" };

	private static final FastDateFormat DATE_TIME_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");

	/** 一秒毫秒数 */
	private static final long SECOND_MILLISECONDS = getConstantValue(1000L);
	/** 一小时毫秒数 */
	private static final long HOUR_SECONDS = getConstantValue(3600L);
	/** 一天小时 */
	private static final long DAY_HOURS = getConstantValue(24L);
	/** 一年天数 */
	private static final long YEAR_DAYS = getConstantValue(365L);
	/** 一小时60分钟 */
	private static final int HOUR_MINUTES = getConstantValue(60);
	/** 一年毫秒数 */
	private static final long YEAR_TIMEMILLIS = getConstantValue(SECOND_MILLISECONDS * HOUR_SECONDS * DAY_HOURS * YEAR_DAYS);
	/** 一天毫秒数 */
	private static final long DAY_TIMEMILLIS = getConstantValue(SECOND_MILLISECONDS * HOUR_SECONDS * DAY_HOURS);
	/** 一小时毫秒数 */
	private static final long HOUR_TIMEMILLIS = getConstantValue(SECOND_MILLISECONDS * HOUR_SECONDS);
	/** 一分钟毫秒数 */
	private static final long MINUTE_TIMEMILLIS = getConstantValue(SECOND_MILLISECONDS * HOUR_MINUTES);
	
	private static final long   DATE_SIZE            = getConstantValue(-1);
	public static final String YYYY_MM_DD           = getConstantValue("yyyy-MM-dd");
	public static final String YYYY_MM_DD_HH_MM_SS  = getConstantValue("yyyy-MM-dd HH:mm:ss");
	public static final String YYYYMMDDHHMMSS  = getConstantValue("yyyyMMddHHmmss");
	private static final int    BIGDECIMAL_DIGITAL_2 = getConstantValue(2);
	
	/**
	 * 值大于0小于10时，左侧补零
	 */
	private static final int TIME_LPAD_ZERO_MAX_VALUE = getConstantValue(10);
	
	
	/**
	 * 传入值，返回一个值，通过这种方式获取常量，可以避免增量发布时，引用常量的地方指向错误
	 *
	 * @param value 传入的作为常量的值
	 * @param <T> 类型参数
	 * @return 常量值
	 */
	public static <T> T getConstantValue(T value) {
		return value;
	}
	
	/**
	 * 格式化日期输出，如果格式为空，则默认为yyyy-MM-dd
	 * 
	 * @param date
	 * @param dateFormat
	 * @return
	 */
	public static String dateToStringFormat(Date date, String dateFormat) {
		if (date == null) {
			return "";
		}

		if (StringUtils.isBlank(dateFormat)) {
			dateFormat = YYYY_MM_DD;
		}

		try {
			SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
			return sdf.format(date);
		} catch (Exception e) {
			LOG.info("INFO", e);
			return null;
		}
	}

	/**
	 * 给定指定模式，指定日期字符串
	 * 
	 * @param format
	 *            模式如(yyyyMMddHHmmss...)
	 * @param date
	 *            日期
	 * @return 解释后返回的日期对象
	 */
	public static Date string2Date(String date, String format) {
		if (StringUtils.isBlank(date)){
			return null;
		}
		if (StringUtils.isBlank(format)) {
			format = YYYY_MM_DD_HH_MM_SS;
		}
		Date parseDate = null;
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
		try {
			parseDate = simpleDateFormat.parse(date);
		} catch (ParseException e) {
			LOG.error(e.getMessage(), e);
		}
		return parseDate;
	}

	/**
	 * get the year of a date
	 * 
	 * @param date
	 *            Date the date which the date get from
	 * @return int the year of the date
	 */
	public static int getYear(Date date) {
		Calendar calendar = createCalendar();
		setCalTime(date, calendar);
		return calendar.get(Calendar.YEAR);
	}

	/**
	 * get the month of a date
	 * 
	 * @param date
	 *            Date the date which the month get from
	 * @return int the month of the date
	 */
	public static int getMonth(Date date) {
		Calendar calendar = createCalendar();
		setCalTime(date, calendar);
		return calendar.get(Calendar.MONTH) + 1;
	}

	/**
	 * get the day of a date
	 * 
	 * @param date
	 *            Date the date which the day get from
	 * @return int the day of the date
	 */
	public static int getDay(Date date) {
		Calendar calendar = createCalendar();
		setCalTime(date, calendar);
		return calendar.get(Calendar.DAY_OF_MONTH);
	}

	private static Calendar createCalendar() {
		Calendar result;
		result = Calendar.getInstance();
		return result;
	}

	private static void setCalTime(Date oldDate, Calendar cal) {
		cal.setTime(oldDate);
	}

	/**
	 * Sets the years field to a date returning a new object. The original date
	 * object is unchanged.
	 * 
	 * @param date
	 *            the date, not null
	 * @param amount
	 *            the amount to set
	 * @return a new Date object set with the specified value
	 */
	public static Date setYears(Date date, int amount) {
		return set(date, Calendar.YEAR, amount);
	}

	/**
	 * Sets the months field to a date returning a new object. The original date
	 * object is unchanged.
	 * 
	 * @param date
	 *            the date, not null
	 * @param amount
	 *            the amount to set
	 * @return a new Date object set with the specified value
	 */
	public static Date setMonths(Date date, int amount) {
		return set(date, Calendar.MONTH, amount);
	}

	/**
	 * Sets the day of month field to a date returning a new object. The
	 * original date object is unchanged.
	 * 
	 * @param date
	 *            the date, not null
	 * @param amount
	 *            the amount to set
	 * @return a new Date object set with the specified value
	 */
	public static Date setDays(Date date, int amount) {
		return set(date, Calendar.DAY_OF_MONTH, amount);
	}

	private static Date set(Date date, int calendarField, int amount) {
		if (date == null) {
			return date;
		}
		Calendar c = Calendar.getInstance();
		c.setLenient(false);
		c.setTime(date);
		c.set(calendarField, amount);
		return c.getTime();
	}

	/**
	 * 将日期用指定的日期格式转换成字符串
	 * 
	 * @param date
	 *            需要转换的日期
	 * @param format
	 *            指定的格式
	 * @return 转换后的字符串
	 */
	public static String date2String(Date date, String format) {
		if (date == null) {
			return null;
		} else {
			if (StringUtils.isEmpty(format)) {
				format = YYYY_MM_DD_HH_MM_SS;
			}
			SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
			return simpleDateFormat.format(date);
		}
	}

	/**
	 * convert a string to a date according to the indicated format.
	 * 
	 * @param sDate
	 *            String the string to be transferred
	 * @param format
	 *            String the indicated format
	 * @return Date the transferred date
	 */
	public static Date toDate(String sDate, String format) {
		if (StringUtils.isEmpty(sDate)) {
			return null;
		}
		return parse(sDate, format);
	}

	/**
	 * convert a string to a date according to the indicated format.
	 * 
	 * @param sDate
	 *            String the string to be transferred
	 * @return Date the transferred date
	 */
	public static Date toDate(String sDate) {
		return toDate(sDate, YYYY_MM_DD);
	}

	private static Date parse(String date, String defaultFormat) {
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(defaultFormat);
		Date resultDate = null;
		try {
			resultDate = simpleDateFormat.parse(date);
		} catch (ParseException e) {
			for (int i = 0; i < optionDateFormats.length; i++) {
				try {
					SimpleDateFormat format = new SimpleDateFormat(optionDateFormats[i]);
					resultDate = format.parse(date);
				} catch (ParseException e2) {
					LOG.error(e2.getMessage(), e2);
				}
			}
		}
		return resultDate;
	}

	/**
	 * 设置系统日期 格式yyyy-MM-dd
	 * 
	 * @return Date
	 */
	public static Date getSysDate() {
		if (defaultDate != null) {
			return defaultDate;
		} else {
			// IFoundationDao foundationDao = (IFoundationDao)
			// SpringBeanUtils.getBean("foundationDao");
			// Date result = foundationDao.getDBDate();
			// 处于性能考虑，采用new Date()的方式获取时间，生产上应确保应用服务器和数据库服务器时间同步
			// IClock clock = SpringBeanUtils.getBean(IClock.class);
			// return clock.getTime();

			return new Date();
		}
	}

	public static void setDefaultDate(Date defaultDate) {
		DateUtils.defaultDate = defaultDate;
	}

	/**
	 * 获取两个日期之前相差的年数据
	 * 
	 * @param startDate
	 *            开始日期
	 * @param endDate
	 *            结束日期
	 * @return 两个日期之间相差的年数，四舍五入
	 */
	public static double getYearSize(Date startDate, Date endDate) {
		if (startDate == null || endDate == null) {
			return Double.valueOf(DATE_SIZE);
		}
		long timMillis = Math.abs(startDate.getTime() - endDate.getTime());
		return BigDecimal.valueOf(timMillis).divide(BigDecimal.valueOf(YEAR_TIMEMILLIS), BIGDECIMAL_DIGITAL_2, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * 获取两个日期之前相差的天数据
	 * 
	 * @param startDate
	 *            开始日期
	 * @param endDate
	 *            结束日期
	 * @return 相差的天数，四舍五入
	 */
	public static double getDaySize(Date startDate, Date endDate) {
		if (startDate == null || endDate == null) {
			return (double) DATE_SIZE;
		}
		long timMillis = endDate.getTime() - startDate.getTime();
		return BigDecimal.valueOf(timMillis).divide(BigDecimal.valueOf(DAY_TIMEMILLIS), BIGDECIMAL_DIGITAL_2, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * 获取两个日期之前相差的小时数据
	 * 
	 * @param startDate
	 *            开始日期
	 * @param endDate
	 *            结束日期
	 * @return 相差的小时数，四舍五入
	 */
	public static double getHourSize(Date startDate, Date endDate) {
		if (startDate == null || endDate == null) {
			return (double) DATE_SIZE;
		}
		long timMillis = Math.abs(startDate.getTime() - endDate.getTime());
		return BigDecimal.valueOf(timMillis).divide(BigDecimal.valueOf(HOUR_TIMEMILLIS), BIGDECIMAL_DIGITAL_2, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * 获取两个时间相差分钟
	 * 
	 * @param startDate
	 *            开始日期
	 * @param endDate
	 *            结束日期
	 * @return
	 */
	public static double getMinuteSize(Date startDate, Date endDate) {
		if (startDate == null || endDate == null) {
			return (double) DATE_SIZE;
		}
		long timMillis = Math.abs(startDate.getTime() - endDate.getTime());
		return BigDecimal.valueOf(timMillis % DAY_TIMEMILLIS % HOUR_TIMEMILLIS).divide(BigDecimal.valueOf(MINUTE_TIMEMILLIS), BIGDECIMAL_DIGITAL_2, BigDecimal.ROUND_HALF_UP)
				.doubleValue();
	}

	/**
	 * 获取两个时间相差分钟
	 * 
	 * @param startMillis
	 *            开始时间毫秒数
	 * @param endMillis
	 *            结束时间毫秒数
	 * @return
	 */
	public static double getMinuteSize(Long startMillis, Long endMillis) {
		if (startMillis == null || endMillis == null) {
			return (double) DATE_SIZE;
		}
		return getMinuteSize(new Date(startMillis), new Date(endMillis));
	}

	/**
	 * 获取两个日期相差的秒数
	 * 
	 * @param startDate
	 *            开始日期
	 * @param endDate
	 *            结束日期
	 * @return 相差的秒数，四舍五入
	 */
	public static long getSecondSize(Date startDate, Date endDate) {
		if (startDate == null) {
			return DATE_SIZE;
		}
		if (endDate == null) {
			endDate = DateUtils.getSysDate();
		}
		long millis = endDate.getTime() - startDate.getTime();
		return millis / SECOND_MILLISECONDS;
	}

	/**
	 * 通过计算出来的剩余时间（含有小数），将其转化为hh:mm格式的剩余时间 例如：1.5小时 --> 01:30
	 * 
	 * @param hour
	 *            剩余时间（hour为单位，可能为负值）
	 * @return 格式化后的剩余时间
	 */
	public static String getTimeInHHMMFormat(BigDecimal hour) {
		int firstIndex = 1;
		String hourString = hour.toString();

		if (hour.compareTo(BigDecimal.ZERO) >= 0) {
			firstIndex = 0;
		}
		if (!hourString.contains(".")) {
			String integerPartString = hourString.substring(firstIndex);
			return (integerPartString.length() == 1 ? "0" + integerPartString : integerPartString) + ":00";
		} else {
			String integerPartString = hourString.substring(firstIndex, hourString.indexOf("."));
			String decimalPart = "0" + hourString.substring(hourString.indexOf("."));
			int decimalPartInt = ((int) Math.ceil(Double.valueOf(decimalPart) * HOUR_MINUTES));
			if (decimalPartInt == HOUR_MINUTES) {
				integerPartString = (Integer.parseInt(integerPartString) + 1) + "";
				decimalPartInt = 0;
			}
			return ((integerPartString.length() == 1 ? "0" + integerPartString : integerPartString) + ":" + (decimalPartInt < TIME_LPAD_ZERO_MAX_VALUE ? "0" + decimalPartInt
					: decimalPartInt));
		}
	}

	/**
	 * 获取当前时间
	 * 
	 * @return currentTime
	 */
	public static Date now() {
		return Calendar.getInstance().getTime();
	}

	public static Date long2Date(Long date, String format) {
		if (StringUtils.isEmpty(format)) {
			format = "yyyy-MM-dd HH:mm:ss";
		}
		Date time = null;
		if (date != null) {
			time = new Date(date);
		}
		return time;
	}

	/**
	 * 将时间毫秒转换String格式
	 * 
	 * @param time
	 *            时间
	 * @param format
	 *            格式化
	 * @return
	 */
	public static String long2Strng(String time, String format) {
		if (StringUtils.isEmpty(format)) {
			format = "yyyy-MM-dd HH:mm:ss";
		}

		if (time != null) {
			try {
				SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
				Long timeMillis = Long.parseLong(time);
				return simpleDateFormat.format(timeMillis);
			} catch (NumberFormatException e) {
				LOG.debug("时间转换异常:{}", e);
			}
		}
		return null;
	}

	public static String long2Strng(String time) {
		return long2Strng(time, null);
	}

	public static String long2Strng(Long time) {
		return long2Strng(time, null);
	}

	/**
	 * 将时间毫秒转换String格式
	 * 
	 * @param time
	 *            时间
	 * @param format
	 *            格式化
	 * @return
	 */
	public static String long2Strng(Long time, String format) {
		if (StringUtils.isEmpty(format)) {
			format = "yyyy-MM-dd HH:mm:ss";
		}

		if (time != null) {
			try {
				SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
				return simpleDateFormat.format(time);
			} catch (NumberFormatException e) {
				LOG.debug("时间转换异常:{}", e);
			}
		}
		return null;
	}

	/**
	 * 得到当前时间
	 * 
	 * @return
	 */
	public static Date getCurrentDate() {
		return new Date();
	}

	/**
	 * 比较两个时间的间隔，单位为毫秒
	 * 
	 * @param endDate
	 * @param startDate
	 * @return
	 */
	public static int diffTwoDateWithTime(Date endDate, Date startDate) {
		if (startDate == null || endDate == null) {
			return -1;
		}
		int diff = (int) (endDate.getTime() - startDate.getTime());

		return diff;
	}
	
	public static int getTimeonDay() {
		Calendar calendar = Calendar.getInstance();
		calendar.set(5, calendar.get(5) + 1);
		calendar.set(11, 0);
		calendar.set(12, 0);
		calendar.set(13, 0);
		return (int) (calendar.getTime().getTime() / 1000L - (new Date()).getTime() / 1000L);
	}

	public static int getTimeonMonth() {
		Calendar calendar = Calendar.getInstance();
		calendar.set(2, calendar.get(2) + 1);
		calendar.set(5, calendar.getMinimum(5));
		calendar.set(11, 0);
		calendar.set(12, 0);
		calendar.set(13, 0);
		return (int) (calendar.getTime().getTime() / 1000L - (new Date()).getTime() / 1000L);
	}
	
	/**
	 * 获取当前周 指定星期几 的日期（返回格式 yyyy-MM-dd）
	 * whatDay 按照周一至周日时间参数规定：0-6，周一为0
	 * @param whatDay
	 * @return
	 */
	public static String getWeekWhatDayOfTime(int whatDay){
		if(whatDay<0||whatDay>6){
			throw new IndexOutOfBoundsException("参数 ["+whatDay+"]不在周一至周日时间范围内");
		}
		SimpleDateFormat sdf = new SimpleDateFormat(YYYY_MM_DD); // 设置时间格式
		Calendar cal = Calendar.getInstance();
	    cal.setTime(new Date());
	    // 判断要计算的日期是否是周日，如果是则减一天计算周六的，否则会出问题，计算到下一周去了  
        int dayWeek = cal.get(Calendar.DAY_OF_WEEK);// 获得当前日期是一个星期的第几天
        if (1 == dayWeek) {
            cal.add(Calendar.DAY_OF_MONTH, -1);
        }
        cal.setFirstDayOfWeek(Calendar.MONDAY);// 设置一个星期的第一天，按中国的习惯一个星期的第一天是星期一
        int day = cal.get(Calendar.DAY_OF_WEEK);// 获得当前日期是一个星期的第几天
		cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - day);// 根据日历的规则，给当前日期减去星期几与一个星期第一天的差值
        cal.add(Calendar.DATE, whatDay);// 周一至周日时间是0-6
		return sdf.format(cal.getTime());
	}
	
	/**
	 * 获取当前月的 第一天日期  （返回格式 yyyy-MM-dd）
	 * @return
	 */
	public static String getMonthFirstDay(){
		SimpleDateFormat sdf = new SimpleDateFormat(YYYY_MM_DD); // 设置时间格式
		//获取当前月第一天：
		Calendar c = Calendar.getInstance();
		c.add(Calendar.MONTH, 0);
		c.set(Calendar.DAY_OF_MONTH,1);//设置为1号,当前日期既为本月第一天
		return sdf.format(c.getTime());
	}
	
	/**
	 * 返回 明天日期  （返回格式 yyyy-MM-dd）
	 * 
	 * @return
	 */
	public static String getNextOneday(){
		SimpleDateFormat sdf = new SimpleDateFormat(YYYY_MM_DD); // 设置时间格式
		Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.add(Calendar.DAY_OF_YEAR, +1);
        return sdf.format(cal.getTime());
	}

	public static Date getTimeMonthsLater(int m) {
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.MONTH, m);
		Timestamp date = Timestamp.valueOf(DATE_TIME_FORMAT.format(calendar.getTime()));
		return date;
	}
}